package com.epoint.arcsoftface.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.TextureView;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.LivenessParam;
import com.arcsoft.face.MaskInfo;
import com.arcsoft.face.enums.CompareModel;
import com.arcsoft.face.enums.DetectFaceOrientPriority;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.ExtractType;
import com.epoint.arcsoftface.R;
import com.epoint.arcsoftface.api.FaceRegonizeCallBack;
import com.epoint.arcsoftface.arcsoft.ConfigUtil;
import com.epoint.arcsoftface.arcsoft.DrawHelper;
import com.epoint.arcsoftface.arcsoft.face.FaceHelper;
import com.epoint.arcsoftface.arcsoft.face.FaceListener;
import com.epoint.arcsoftface.arcsoft.face.LivenessType;
import com.epoint.arcsoftface.arcsoft.face.RecognizeColor;
import com.epoint.arcsoftface.arcsoft.face.RequestFeatureStatus;
import com.epoint.arcsoftface.arcsoft.face.RequestLivenessStatus;
import com.epoint.arcsoftface.arcsoft.model.DrawInfo;
import com.epoint.arcsoftface.arcsoft.model.FacePreviewInfo;
import com.epoint.arcsoftface.arcsoft.widget.FaceRectView;
import com.epoint.common.base.ComomAction;
import com.epoint.common.camera.CameraHelper;
import com.epoint.common.camera.CameraListener;
import com.epoint.common.utils.BitmapUtils;
import com.epoint.common.utils.FileConfig;
import com.epoint.common.view.TextureVideoViewOutlineProvider;
import com.epoint.core.application.AppUtil;
import com.epoint.core.util.device.DensityUtil;
import com.epoint.core.util.easythread.AsyncCallback;
import com.epoint.core.util.task.FrmAsynTask;
import com.epoint.ui.widget.toast.ToastUtil;
import com.google.gson.JsonObject;

import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import androidx.annotation.Nullable;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * @author:张健
 * @date: 2023/7/13
 * @description: 虹软人脸识别的容器
 */
public class ArcsoftFaceRecognizeContainer extends RelativeLayout implements CameraListener, FaceListener {

    private boolean isInit = false;  //人证初始化成功

    private boolean maskDetect = false;

    //比对阈值，建议为0.82
    private static float THRESHOLD = 0.7f;

    /**
     * VIDEO模式人脸检测引擎，用于预览帧人脸追踪
     */
    private FaceEngine ftEngine;
    /**
     * 用于特征提取的引擎
     */
    private FaceEngine frEngine;
    /**
     * IMAGE模式活体检测引擎，用于预览帧人脸活体检测
     */
    private FaceEngine flEngine;

    private static final int MAX_DETECT_NUM = 10;

    private int ftInitCode = -1;
    private int frInitCode = -1;
    private int flInitCode = -1;

    //存储特征值集合
    private List<FaceFeature> faceFeatures = new ArrayList();

    private DrawHelper drawHelper;
    private FaceHelper faceHelper;


    TextureView cameraView = null;

    //线程运行数据
    private FrmAsynTask task;

    boolean waitIdCardFeature = false;  //等待身份证特征获取成功

    /**
     * 活体检测的开关
     */
    private boolean livenessDetect = false;

    /**
     * 绘制人脸框的控件
     */
    private FaceRectView faceRectView;


    /**
     * 用于记录人脸识别相关状态
     */
    private ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();
    /**
     * 用于记录人脸特征提取出错重试次数
     */
    private ConcurrentHashMap<Integer, Integer> extractErrorRetryMap = new ConcurrentHashMap<>();
    /**
     * 用于存储活体值
     */
    private ConcurrentHashMap<Integer, Integer> livenessMap = new ConcurrentHashMap<>();
    /**
     * 用于存储活体检测出错重试次数
     */
    private ConcurrentHashMap<Integer, Integer> livenessErrorRetryMap = new ConcurrentHashMap<>();

    FaceRegonizeCallBack callBack = null;

    /**
     * 当FR成功，活体未成功时，FR等待活体的时间
     */
    private static final int WAIT_LIVENESS_INTERVAL = 100;
    /**
     * 失败重试间隔时间（ms）
     */
    private static final long FAIL_RETRY_INTERVAL = 1000;
    /**
     * 出错重试最大次数
     */
    private static final int MAX_RETRY_TIME = 3;

    private CompositeDisposable getFeatureDelayedDisposables = new CompositeDisposable();
    private CompositeDisposable delayFaceTaskCompositeDisposable = new CompositeDisposable();

    CameraHelper cameraHelper = null;

    private Camera.Size previewSize;

    public int rotation = 0;

    //是否圆角展示画面
    public int radius = 0;
    //宽度
    public int width = 0;
    //高度
    public int height = 0;
    //坐标
    public int x = 0;
    //坐标
    public int y = 0;

    public void setRotation(int rotation) {
        this.rotation = rotation;
    }

    public ArcsoftFaceRecognizeContainer(Context context) {
        super(context);

        init();
    }

    /**
     * 设置人脸识别
     */
    public void setFaceRegonizeCallBack(FaceRegonizeCallBack callBack) {
        this.callBack = callBack;
    }

    public ArcsoftFaceRecognizeContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        activeAndInitFaceEngine();

        cameraView = new TextureView(getContext());
        cameraView.setTag("camera");
        if (radius > 0) {
            cameraView.setOutlineProvider(new TextureVideoViewOutlineProvider(DensityUtil.dip2px(getContext(), radius)));
            cameraView.setClipToOutline(true);
        }
        RelativeLayout.LayoutParams params = null;
        if (width == 0 && height == 0) {
            params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            params.leftMargin = x;
            params.topMargin = y;
        } else {
            params = new RelativeLayout.LayoutParams(width, height);
            params.leftMargin = x;
            params.topMargin = y;
        }
        addView(cameraView, params);
        faceRectView = new FaceRectView(getContext());
        faceRectView.setTag("facerect");
        addView(faceRectView, params);
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getViewTreeObserver().removeOnGlobalLayoutListener(this);

                cameraHelper = new CameraHelper.Builder()
                        .previewViewSize(new Point(getMeasuredWidth(), getMeasuredHeight()))
                        .previewSize(new Point(1280, 720))
                        .rotation(rotation)
                        .specificCameraId(0)
                        .isMirror(false)
                        .previewOn(cameraView)
                        .cameraListener(ArcsoftFaceRecognizeContainer.this)
                        .build();
                cameraHelper.init();
                cameraHelper.start();
            }
        });


    }

    /**
     * 激活并且初始化人脸识别引擎
     */
    private void activeAndInitFaceEngine() {
        if (!new File(FileConfig.getStoragePath() + File.separator + "active_result.dat").exists()) {
            ToastUtil.toastShort(getContext(), "离线授权文件丢失，授权失败");
            return;
        }
        int activeCode = FaceEngine.activeOffline(AppUtil.getApplication(),
                FileConfig.getStoragePath() + File.separator + "active_result.dat");
        if (activeCode != 0 && activeCode != 90114) {
            ToastUtil.toastShort(getContext(), "人脸引擎激活失败：" + activeCode);
            ComomAction.writeLogThread("zjiankf", "人脸引擎激活失败：" + activeCode);
        } else {
            //初始化
            ftEngine = new FaceEngine();
            if (maskDetect) {
                ftInitCode = ftEngine.init(getContext(), DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_0_ONLY,
                        16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_FACE_SHELTER | FaceEngine.ASF_MASK_DETECT | FaceEngine.ASF_FACELANDMARK);

            } else {
                ftInitCode = ftEngine.init(getContext(), DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_0_ONLY,
                        16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT);
            }


            frEngine = new FaceEngine();
            frInitCode = frEngine.init(getContext(), DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
                    16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_FACE_DETECT);

            flEngine = new FaceEngine();
            flInitCode = flEngine.init(getContext(), DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
                    16, MAX_DETECT_NUM, FaceEngine.ASF_LIVENESS);


            if (ftInitCode != ErrorInfo.MOK) {
                ComomAction.writeLogThread("zjiankf", "人脸引擎ftEngine初始化失败：" + ftInitCode);
            }
            if (frInitCode != ErrorInfo.MOK) {
                ComomAction.writeLogThread("zjiankf", "人脸引擎frEngine初始化失败：" + frInitCode);
            }
            if (flInitCode != ErrorInfo.MOK) {
                ComomAction.writeLogThread("zjiankf", "人脸引擎flEngine初始化失败：" + flInitCode);
            }
            isInit = (ftInitCode == ErrorInfo.MOK && frInitCode == ErrorInfo.MOK && flInitCode == ErrorInfo.MOK);
        }

    }

    /**
     * 提取身份证图片信息到模板
     *
     * @param bitmap
     */
    public void inputIdCard(Bitmap bitmap) {
        if (bitmap == null) {
            return;
        }
        if (this.task == null) {
            this.task = new FrmAsynTask();
        }
        this.task.start(new Callable() {
            public Object call() throws Exception {
                ArcsoftFaceRecognizeContainer.this.getFaceFeature(bitmap);
                return new JsonObject();
            }
        }, new AsyncCallback<JsonObject>() {
            public void onSuccess(JsonObject obj) {
                waitIdCardFeature = true;
            }

            public void onFailed(Throwable t) {
                if (callBack != null) {
                    callBack.refreshViewWithFailure(-1, t.getMessage(), (JsonObject) null);
                }
            }
        });
    }

    /**
     * 获取传入图片的特征值 与采集照进行比对
     *
     * @param bitmap
     * @throws
     */
    private void getFaceFeature(Bitmap bitmap) throws Exception {
        if (!isInit) {
            throw new Exception(getContext().getString(R.string.str_uninit));
        }
        if (bitmap == null) {
            throw new Exception(getContext().getString(R.string.str_error_noface));
        } else if (this.frEngine == null) {
            throw new Exception(getContext().getString(R.string.str_uninit));
        } else {
            bitmap = BitmapUtils.alignBitmapForNv21(bitmap);
            if (bitmap == null) {
                throw new Exception(getContext().getString(R.string.str_bitmap_error));
            } else {
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                byte[] nv21 = BitmapUtils.bitmapToNv21(bitmap, width, height);
                if (nv21 != null) {
                    List<FaceInfo> faceInfoList = new ArrayList();
                    int detectCode = this.frEngine.detectFaces(nv21, width, height, 2050, faceInfoList);
                    if (detectCode == 0 && faceInfoList.size() != 0) {
                        Iterator var7 = faceInfoList.iterator();
                        while (var7.hasNext()) {
                            FaceInfo faceInfo = (FaceInfo) var7.next();
                            FaceFeature faceFeature = new FaceFeature();
                            int res = this.frEngine.extractFaceFeature(nv21, width, height, 2050, faceInfo, ExtractType.RECOGNIZE, MaskInfo.NOT_WORN, faceFeature);
                            if (res == 0) {
                                this.faceFeatures.add(faceFeature);
                            }
                        }
                    } else {
                        throw new Exception("传入的身份证未检测到人脸");
                    }
                } else {
                    throw new Exception(getContext().getString(R.string.str_bitmap_error));
                }
            }
        }
    }

    public void detachView() {
        if (cameraHelper != null) {
            cameraHelper.release();
        }
        if (ftInitCode == ErrorInfo.MOK && ftEngine != null) {
            synchronized (ftEngine) {
                int ftUnInitCode = ftEngine.unInit();
            }
        }
        if (frInitCode == ErrorInfo.MOK && frEngine != null) {
            synchronized (frEngine) {
                int frUnInitCode = frEngine.unInit();
            }
        }
        if (flInitCode == ErrorInfo.MOK && flEngine != null) {
            synchronized (flEngine) {
                int flUnInitCode = flEngine.unInit();
            }
        }

        if (getFeatureDelayedDisposables != null) {
            getFeatureDelayedDisposables.clear();
        }
        if (delayFaceTaskCompositeDisposable != null) {
            delayFaceTaskCompositeDisposable.clear();
        }
        if (faceHelper != null) {
            ConfigUtil.setTrackedFaceCount(getContext(), faceHelper.getTrackedFaceCount());
            faceHelper.release();
            faceHelper = null;
        }
        if (faceFeatures != null) {
            faceFeatures.clear();
            faceFeatures = null;
        }
    }

    @Override
    public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
        Camera.Size lastPreviewSize = previewSize;
        previewSize = camera.getParameters().getPreviewSize();
        drawHelper = new DrawHelper(previewSize.width, previewSize.height, cameraView.getWidth(), cameraView.getHeight(), displayOrientation
                , cameraId, isMirror, false, false);
        // 切换相机的时候可能会导致预览尺寸发生变化
        if (faceHelper == null ||
                lastPreviewSize == null ||
                lastPreviewSize.width != previewSize.width || lastPreviewSize.height != previewSize.height) {
            Integer trackedFaceCount = null;
            // 记录切换时的人脸序号
            if (faceHelper != null) {
                trackedFaceCount = faceHelper.getTrackedFaceCount();
                faceHelper.release();
            }
            faceHelper = new FaceHelper.Builder()
                    .ftEngine(ftEngine)
                    .frEngine(frEngine)
                    .flEngine(flEngine)
                    .frQueueSize(MAX_DETECT_NUM)
                    .flQueueSize(MAX_DETECT_NUM)
                    .previewSize(previewSize)
                    .faceListener(this)
                    .trackedFaceCount(trackedFaceCount == null ? ConfigUtil.getTrackedFaceCount(getContext()) : trackedFaceCount)
                    .build();
        }
    }

    @Override
    public void onPreview(byte[] data, Camera camera) {
//        Log.e("zjiankf",previewSize.width+":"+previewSize.height);
        if (isInit) {
            data = BitmapUtils.NV21_rotate_to_90(data, previewSize.width, previewSize.height);
            //初始化成功之后,绘制人脸。
            List<FacePreviewInfo> facePreviewInfoList = null;
            if (maskDetect) {
                facePreviewInfoList = faceHelper.onPreviewFrame(data, 90);
            } else {
                facePreviewInfoList = faceHelper.onPreviewFrameNoMask(data, 90);
            }
            if (facePreviewInfoList != null && faceRectView != null && drawHelper != null) {
                drawPreviewInfo(facePreviewInfoList);
            }
            clearLeftFace(facePreviewInfoList);
            if (facePreviewInfoList != null && facePreviewInfoList.size() > 0 && previewSize != null && waitIdCardFeature) {
                for (int i = 0; i < facePreviewInfoList.size(); i++) {
                    Integer status = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());
                    if (maskDetect) {
                        int mask = facePreviewInfoList.get(i).getMask();
                        if (mask == MaskInfo.WORN || mask == MaskInfo.UNKNOWN) {
                            continue;
                        }
                    }
                    /**
                     * 在活体检测开启，在人脸识别状态不为成功或人脸活体状态不为处理中（ANALYZING）且不为处理完成（ALIVE、NOT_ALIVE）时重新进行活体检测
                     */
                    if (livenessDetect && (status == null || status != RequestFeatureStatus.SUCCEED)) {
                        Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());
                        if (liveness == null
                                || (liveness != LivenessInfo.ALIVE && liveness != LivenessInfo.NOT_ALIVE && liveness != RequestLivenessStatus.ANALYZING)) {
                            livenessMap.put(facePreviewInfoList.get(i).getTrackId(), RequestLivenessStatus.ANALYZING);
                            faceHelper.requestFaceLiveness(data, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId(), LivenessType.RGB);
                        }
                    }
                    /**
                     * 对于每个人脸，若状态为空或者为失败，则请求特征提取（可根据需要添加其他判断以限制特征提取次数），
                     * 特征提取回传的人脸特征结果在{@link FaceListener#onFaceFeatureInfoGet(FaceFeature, Integer, Integer)}中回传
                     */
                    if (status == null
                            || status == RequestFeatureStatus.TO_RETRY) {
                        requestFeatureStatusMap.put(facePreviewInfoList.get(i).getTrackId(), RequestFeatureStatus.SEARCHING);

                        if (maskDetect) {
                            faceHelper.requestFaceFeature(data, facePreviewInfoList.get(i).getFaceInfo(), facePreviewInfoList.get(i).getMask(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId());
                        } else {

                            faceHelper.requestFaceFeature(data, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId());

                        }
                    }
                }
            }
        }
    }

    @Override
    public void onCameraClosed() {

    }

    @Override
    public void onCameraError(Exception e) {

    }

    @Override
    public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
        if (drawHelper != null) {
            drawHelper.setCameraDisplayOrientation(displayOrientation);
        }
    }

    @Override
    public void onFail(Exception e) {

    }

    @Override
    public void onFaceFeatureInfoGet(@Nullable FaceFeature faceFeature, Integer requestId, Integer errorCode, byte[] nv21) {
//FR成功
        if (faceFeature != null) {
//                    Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId);
            Integer liveness = livenessMap.get(requestId);
            //不做活体检测的情况，直接搜索
            if (!livenessDetect) {
                searchFace(faceFeature, requestId, nv21);
            }
            //活体检测通过，搜索特征
            else if (liveness != null && liveness == LivenessInfo.ALIVE) {
                searchFace(faceFeature, requestId, nv21);
            }

            //活体检测未出结果，或者非活体，延迟执行该函数
            else {
                if (requestFeatureStatusMap.containsKey(requestId)) {
                    Observable.timer(WAIT_LIVENESS_INTERVAL, TimeUnit.MILLISECONDS)
                            .subscribe(new Observer<Long>() {
                                Disposable disposable;

                                @Override
                                public void onSubscribe(Disposable d) {
                                    disposable = d;
                                    getFeatureDelayedDisposables.add(disposable);
                                }

                                @Override
                                public void onNext(Long aLong) {
                                    onFaceFeatureInfoGet(faceFeature, requestId, errorCode, nv21);
                                }

                                @Override
                                public void onError(Throwable e) {

                                }

                                @Override
                                public void onComplete() {
                                    getFeatureDelayedDisposables.remove(disposable);
                                }
                            });
                }
            }

        }
        //特征提取失败
        else {
            if (increaseAndGetValue(extractErrorRetryMap, requestId) > MAX_RETRY_TIME) {
                extractErrorRetryMap.put(requestId, 0);
                ComomAction.writeLogThread("人证比对结果", "人脸置信度低，特征提取失败");
                // 在尝试最大次数后，特征提取仍然失败，则认为识别未通过
                requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
                retryRecognizeDelayed(requestId);
            } else {
                requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);
            }
        }
    }

    @Override
    public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo,  byte[] nv21, FaceInfo faceInfo,Integer requestId, Integer errorCode) {
        if (livenessInfo != null) {
            int liveness = livenessInfo.getLiveness();
            livenessMap.put(requestId, liveness);
            Log.e("zjiankf", requestId + "    :   " + liveness);
            // 非活体，重试
            if (liveness == LivenessInfo.NOT_ALIVE) {
                retryLivenessDetectDelayed(requestId);
            }
        } else {
            if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {
                livenessErrorRetryMap.put(requestId, 0);
                retryLivenessDetectDelayed(requestId);
            } else {
                livenessMap.put(requestId, LivenessInfo.UNKNOWN);
            }
        }
    }


    /**
     * 人脸框绘制
     *
     * @param facePreviewInfoList
     */
    private void drawPreviewInfo(List<FacePreviewInfo> facePreviewInfoList) {
        List<DrawInfo> drawInfoList = new ArrayList<>();
        for (int i = 0; i < facePreviewInfoList.size(); i++) {
            String name = faceHelper.getName(facePreviewInfoList.get(i).getTrackId());
            Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());
            Integer recognizeStatus = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());
            int color = RecognizeColor.COLOR_UNKNOWN;
            if (maskDetect) {

                // 根据识别结果和活体结果设置颜色

                //true 是没口罩
                boolean hasMask = facePreviewInfoList.get(i).getMask() == MaskInfo.WORN || facePreviewInfoList.get(i).getMask() == MaskInfo.UNKNOWN;

                if (hasMask) {
                    color = RecognizeColor.COLOR_FAILED;
                    name = "请摘除口罩";
                } else {


                    if (recognizeStatus != null) {
                        if (recognizeStatus == RequestFeatureStatus.FAILED) {
                            color = RecognizeColor.COLOR_FAILED;
                        }
                        if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
                            color = RecognizeColor.COLOR_SUCCESS;
                        }
                    }
                    if (liveness != null && liveness != LivenessInfo.ALIVE) {
                        color = RecognizeColor.COLOR_FAILED;
                        name = "活体检测未通过";
                    }
                }
            } else {
                color = RecognizeColor.COLOR_UNKNOWN;
                if (recognizeStatus != null) {
                    if (recognizeStatus == RequestFeatureStatus.FAILED) {
                        color = RecognizeColor.COLOR_FAILED;
                    }
                    if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
                        color = RecognizeColor.COLOR_SUCCESS;
                    }
                }
                if (liveness != null && liveness != LivenessInfo.ALIVE) {
                    color = RecognizeColor.COLOR_FAILED;
                    name = "活体检测未通过";
                }
            }


            drawInfoList.add(new DrawInfo(drawHelper.adjustRect(facePreviewInfoList.get(i).getFaceInfo().getRect()),
                    GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE, liveness == null ? LivenessInfo.UNKNOWN : liveness, color,
                    name));
        }
        drawHelper.draw(faceRectView, drawInfoList);
    }

    /**
     * 删除已经离开的人脸
     *
     * @param facePreviewInfoList 人脸和trackId列表
     */
    private void clearLeftFace(List<FacePreviewInfo> facePreviewInfoList) {
        if (facePreviewInfoList == null || facePreviewInfoList.size() == 0) {
            requestFeatureStatusMap.clear();
            livenessMap.clear();
            livenessErrorRetryMap.clear();
            extractErrorRetryMap.clear();
            if (getFeatureDelayedDisposables != null) {
                getFeatureDelayedDisposables.clear();
            }
            return;
        }
        Enumeration<Integer> keys = requestFeatureStatusMap.keys();
        while (keys.hasMoreElements()) {
            int key = keys.nextElement();
            boolean contained = false;
            for (FacePreviewInfo facePreviewInfo : facePreviewInfoList) {
                if (facePreviewInfo.getTrackId() == key) {
                    contained = true;
                    break;
                }
            }
            if (!contained) {
                requestFeatureStatusMap.remove(key);
                livenessMap.remove(key);
                livenessErrorRetryMap.remove(key);
                extractErrorRetryMap.remove(key);
            }
        }
    }

    /**
     * 将map中key对应的value增1回传
     *
     * @param countMap map
     * @param key      key
     * @return 增1后的value
     */
    public int increaseAndGetValue(Map<Integer, Integer> countMap, int key) {
        if (countMap == null) {
            return 0;
        }
        Integer value = countMap.get(key);
        if (value == null) {
            value = 0;
        }
        countMap.put(key, ++value);
        return value;
    }


    /**
     * 延迟 FAIL_RETRY_INTERVAL 重新进行人脸识别
     *
     * @param requestId 人脸ID
     */
    private void retryRecognizeDelayed(final Integer requestId) {
        requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
        Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)
                .subscribe(new Observer<Long>() {
                    Disposable disposable;

                    @Override
                    public void onSubscribe(Disposable d) {
                        disposable = d;
                        delayFaceTaskCompositeDisposable.add(disposable);
                    }

                    @Override
                    public void onNext(Long aLong) {

                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onComplete() {

                        requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);
                        delayFaceTaskCompositeDisposable.remove(disposable);
                    }
                });
    }


    /**
     * 延迟 FAIL_RETRY_INTERVAL 重新进行活体检测
     *
     * @param requestId 人脸ID
     */
    private void retryLivenessDetectDelayed(final Integer requestId) {
        Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)
                .subscribe(new Observer<Long>() {
                    Disposable disposable;

                    @Override
                    public void onSubscribe(Disposable d) {
                        disposable = d;
                        delayFaceTaskCompositeDisposable.add(disposable);
                    }

                    @Override
                    public void onNext(Long aLong) {

                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onComplete() {
                        livenessMap.put(requestId, LivenessInfo.UNKNOWN);
                        delayFaceTaskCompositeDisposable.remove(disposable);
                    }
                });
    }

    /**
     * ---------------------------------人脸搜索---------------------------------------
     **/

    private void searchFace(final FaceFeature frFace, final Integer requestId, byte[] nv21) {
        if (callBack != null) {
            callBack.onGetFaceCallBack(nv21);
        }

        Observable
                .create(new ObservableOnSubscribe<Float>() {
                    @Override
                    public void subscribe(ObservableEmitter<Float> emitter) {
                        Float compareResult = getTopOfFaceLib(frFace);
                        emitter.onNext(compareResult);
                    }
                })
                .subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Float>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }

                    @Override
                    public void onNext(Float compareResult) {
                        if (compareResult == null || compareResult.floatValue() < 0) {
                            requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
                            return;
                        }
                        if (compareResult.floatValue() > THRESHOLD) {
                            ComomAction.writeLogThread("人证比对结果", "compareFeature: result " + compareResult.floatValue()
                                    + ",isSuccess "
                                    + true);
                            JsonObject object = new JsonObject();
                            object.addProperty("faceBase64", BitmapUtils.bitmapToBase64(BitmapUtils.nv21ToBitmap(nv21, previewSize.height, previewSize.width)));
                            object.addProperty("score", compareResult.floatValue());
                            if (callBack != null) {
                                //缓存身份证
                                callBack.refreshViewWithSuccess(object);
                            }
                            requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);
                        } else {
                            ComomAction.writeLogThread("人证比对结果", "compareFeature: result " + compareResult.floatValue()
                                    + ",isSuccess "
                                    + false + "重新识别");

                            if (callBack != null) {
                                callBack.refreshViewWithFailureScore(compareResult.floatValue());
                            }

                            retryRecognizeDelayed(requestId);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        retryRecognizeDelayed(requestId);
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    /**
     * 是否正在搜索人脸，保证搜索操作单线程进行
     */
    private boolean isProcessing = false;

    /**
     * 在特征库中搜索
     *
     * @param faceFeature 传入特征数据
     * @return 比对结果
     */
    public Float getTopOfFaceLib(FaceFeature faceFeature) {
        if (frEngine == null || isProcessing || faceFeature == null || faceFeatures == null || faceFeatures.size() == 0) {
            return new Float(-1);
        }


        FaceSimilar faceSimilar = new FaceSimilar();
        float maxSimilar = 0.0F;
        int maxSimilarIndex = -1;
        this.isProcessing = true;
        for (int i = 0; i < this.faceFeatures.size(); ++i) {
            this.frEngine.compareFaceFeature(faceFeature, (FaceFeature) this.faceFeatures.get(i), CompareModel.ID_CARD, faceSimilar);
            if (faceSimilar.getScore() > maxSimilar) {
                maxSimilar = faceSimilar.getScore();
                maxSimilarIndex = i;
            }
        }
        isProcessing = false;
        if (maxSimilarIndex != -1) {
            return maxSimilar;
        }
        return new Float(-1);
    }


}
